package com.xiaohong.security.config;


import com.xiaohong.security.handle.MyAccessDeniedHandler;
import com.xiaohong.security.handle.MyLogoutSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.sql.DataSource;

/**
 * @author : xiaohong
 * @date : 2021/4/29 16:54
 * SpringSecurity配置类
 */
@Configuration
@SuppressWarnings("all")
public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired
    private MyAccessDeniedHandler myAccessDeniedHandler;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

//    @Autowired
//    private DataSource dataSource; // 数据源


    /**
     * 重写WebSecurityConfigurerAdapter里的configure方法
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 表单提交
        http.formLogin()
                // 自定义用户名name（提交表单的）
                .usernameParameter("nickName")
                // 自定义密码name（提交表单的）
                .passwordParameter("psd")
                // 当发现/login时认为时登录，必须和表单提交的地址一样，去执行UserDetailsServiceImpl
                .loginProcessingUrl("/login")
                //自定义登录页面
                .loginPage("/login.html")
                // 登录成功后跳转 必须时POST请求,所以不能直接跳转页面
                .successForwardUrl("/toSuccess");
//                .successHandler(new MyAuthenticationSuccessHandler("https://www.baidu.com"))
                // 登录失败跳转 必须时POST请求,所以不能直接跳转页面 记住这里不能直接写/error /error是系统默认错误页面
//                .failureForwardUrl("/toError");
//                .failureHandler(new MyAuthenticationFailureHandler("https://www.iconfont.cn"));

        // 请求权限控制
        http.authorizeRequests()
                // 放行登录页面请求
//                .antMatchers("/login.html").permitAll()
                .antMatchers("/error.html").permitAll()
                .antMatchers("/showLogin").permitAll()
                .antMatchers("/showLogout").permitAll()
                // 等价于
//                .antMatchers("/error.html").access("permitAll()")
                // 静态资源放行
//                .antMatchers("/js/**","/css/**","/images/**").permitAll()
                // 正则匹配放行 (可以指定请求方法，也可以省略)
//                .regexMatchers(HttpMethod.GET,"/test").permitAll()
                // 对于全局请求前缀（servlet.Path）放行
//                .mvcMatchers("/login").servletPath("/security").permitAll()
                // 等效于
//                .antMatchers("/security/login").permitAll()
                // 具有指定权限的用户才可访问 权限名称严格区分大小写
//                .antMatchers("/test.html").hasAuthority("admin")
                // 如果用户拥有给定权限里的一个便可访问 权限名称严格区分大小写
//                .antMatchers("/test.html").hasAnyAuthority("admin","user")
                // 具有指定角色的用户才可访问 角色名称严格区分大小写 不允许ROLE_开头
//                .antMatchers("/test.html").hasRole("xiaohong")
                // 如果用户拥有给定角色里的一个便可访问 角色名称严格区分大小写
//                .antMatchers("/test.html").hasAnyAuthority("xiaohong","xiaoming")
                // 指定IP才可访问
//                .antMatchers("/test.html").hasIpAddress("127.0.0.1")
                // 使用自定义的判断方法
//                .antMatchers("/test.html").access("@myServiceImpl.hasPerminssion(request,authentication)")
                // 其他所有的请求都需要鉴权
                .anyRequest().authenticated();

       /* 内置访问方法介绍：
        permitAll 允许任何人访问
        denyAll 不允许任何人访问
        anonymous 可以匿名访问
        authenticated 所有人需要认证访问
        fullyAuthenticated 勾选了记住我也不能访问，需要登录
        rememberMe 勾选了记住我的才能访问*/


        // 关闭csrf防火墙 如果不关闭，需要前台将后台生成的_csrf.token传过来
//        http.csrf().disable();


        // 异常处理
        http.exceptionHandling()
                // 自定义异常展示信息
                .accessDeniedHandler(myAccessDeniedHandler);

        // 记住我
        http.rememberMe()
                // 失效时间，默认2周，单位秒
                .tokenValiditySeconds(60)
                // 记住我参数名 默认remember-me
                .rememberMeParameter("remember")
                // 自定义登录逻辑
                .userDetailsService(userDetailsService)
                //持久层对象
                .tokenRepository(persistentTokenRepository);

        // 退出
        http.logout()
                // 自定义退出路径 默认是/logout
                .logoutUrl("/user/logout")
                // 退出登录跳转页面 可以自定义
                .logoutSuccessHandler(new MyLogoutSuccessHandler("/showLogin"));
//                .logoutSuccessUrl("/test.html");

    }


    /**
     * 创建一个持久化用户登录标记的Repository对象
     * 所有被@Bean注解描述的方法，其参数，都自动从Spring容器中获取对象
     * 如果没有对象或者对象不唯一，抛出异常
     * @param dataSource
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        // 设置数据源
        jdbcTokenRepository.setDataSource(dataSource);
        // 自动建表，第一次启动时需要，第二次启动时注释掉
//        jdbcTokenRepository.setCreateTableOnStartup(true);
        return jdbcTokenRepository;
    }

}
